import type {ExtendedModalApi, ModalApiOptions, ModalProps} from './modal';

import {defineComponent, h, inject, nextTick, provide, reactive} from 'vue';

import {useStore} from '@/utils/shared/store';

import JcxModal from './modal.vue';
import {ModalApi} from './modal-api';

const USER_MODAL_INJECT_KEY = Symbol('JCX_MODAL_INJECT');

export function useJcxModal<TParentModalProps extends ModalProps = ModalProps>(
    options: ModalApiOptions = {},
) {
    // Modal一般会抽离出来，所以如果有传入 connectedComponent，则表示为外部调用，与内部组件进行连接
    // 外部的Modal通过provide/inject传递api

    const {connectedComponent} = options;
    if (connectedComponent) {
        const extendedApi = reactive({});
        const Modal = defineComponent(
            (props: TParentModalProps, {attrs, slots}) => {
                provide(USER_MODAL_INJECT_KEY, {
                    extendApi(api: ExtendedModalApi) {
                        // 不能直接给 reactive 赋值，会丢失响应
                        // 不能用 Object.assign,会丢失 api 的原型函数
                        Object.setPrototypeOf(extendedApi, api);
                    },
                    options,
                });
                checkProps(extendedApi as ExtendedModalApi, {
                    ...props,
                    ...attrs,
                    ...slots,
                });
                return () =>
                    h(
                        connectedComponent,
                        {
                            ...props,
                            ...attrs,
                        },
                        slots,
                    );
            },
            {
                inheritAttrs: false,
                name: 'JcxParentModal',
            },
        );
        return [Modal, extendedApi as ExtendedModalApi] as const;
    }

    const injectData = inject<any>(USER_MODAL_INJECT_KEY, {});

    const mergedOptions = {
        ...injectData.options,
        ...options,
    } as ModalApiOptions;

    mergedOptions.onOpenChange = (isOpen: boolean) => {
        options.onOpenChange?.(isOpen);
        injectData.options?.onOpenChange?.(isOpen);
    };
    const api = new ModalApi(mergedOptions);

    const extendedApi: ExtendedModalApi = api as never;

    extendedApi.useStore = (selector) => {
        return useStore(api.store, selector);
    };

    const Modal = defineComponent(
        (props: ModalProps, {attrs, slots}) => {
            return () =>
                h(
                    JcxModal,
                    {
                        ...props,
                        ...attrs,
                        modalApi: extendedApi,
                    },
                    slots,
                );
        },
        {
            inheritAttrs: false,
            name: 'JcxModal',
        },
    );
    injectData.extendApi?.(extendedApi);
    return [Modal, extendedApi] as const;
}

async function checkProps(api: ExtendedModalApi, attrs: Record<string, any>) {
    if (!attrs || Object.keys(attrs).length === 0) {
        return;
    }
    await nextTick();

    const state = api?.store?.state;

    if (!state) {
        return;
    }

    const stateKeys = new Set(Object.keys(state));

    for (const attr of Object.keys(attrs)) {
        if (stateKeys.has(attr) && !['class'].includes(attr)) {
            // connectedComponent存在时，不要传入Modal的props，会造成复杂度提升，如果你需要修改Modal的props，请使用 useModal 或者api
            console.warn(
                `[Jcx Modal]: When 'connectedComponent' exists, do not set props or slots '${attr}', which will increase complexity. If you need to modify the props of Modal, please use useJcxModal or api.`,
            );
        }
    }
}
